package tr.muyat.kablosuzizci.istemci.iletisimmodulu;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import tr.muyat.kablosuzizci.istemci.yardimcilarmodulu.Kullanislilar;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

/**
 * Bluetooth bağlantısını hizmeti sunan sınıf. Android Bluetooth APIsini
 * kullanır.
 * 
 * @author Fatih Ulusoy
 */
public class BluetoothBaglantisi implements IBaglanti {
	
	// Debug icin
	private static final String TAG = BluetoothBaglantisi.class.getSimpleName();

	private static final int EXIT_CMD = -1;
	
    // Message types sent for sensor handler
    private static final int MOVEMENT_DATA = 1;
    private static final int INIT_HANDLER = 2;
    private static final int STOP_HANDLER = 3;
    
    private static final int MESSAGE_READ = 5;

	// Bluetooth altyapisinda yaratilacak servisin UUIDsi
	private final UUID servisUUID;
    
	// Uzak bluetooth cihazinin MAC adresi
	private final String uzakCihazinAdresi;
	
	// Baglantinin baslayip baslamadığını tutar
    private boolean baglantiBasladimi = false;
	
	// Baglantıdan gelen verileri dinler
	private IBaglantiDinleyici baglantiDinleyici;
	
    // Uzak bluetooth cihazının vekil nesnesi
	private BluetoothDevice uzakBluetoothCihazi;
	
    // Yerel bluetooth adaptoru
    private BluetoothAdapter bluetoothAdaptoru = null;  
    
    // Yollanacak verileri işler
	private BluetoothBaglantisiVeriIsleyici veriIsleyici;
	
	
	/**
	 * Bluetooth bağlantısı nesnesini yaratır. Ama bu yaratılan nesne daha veri alıp-vermeye
	 * hazır değildir. 
	 * 
	 * @param baglanilacakCihazinMACadresi
	 * @param bluetoothServisUUID
	 * @param baglantiDinleyici
	 */
	protected BluetoothBaglantisi(String baglanilacakCihazinMACadresi,
			String bluetoothServisUUID, IBaglantiDinleyici baglantiDinleyici) {
		this.baglantiDinleyici = baglantiDinleyici;
		uzakCihazinAdresi = baglanilacakCihazinMACadresi;
		servisUUID = UUID.fromString(bluetoothServisUUID);
	}
	

	/**
	 * Bluetooth bağlantısını başlatır. Bu method çağrıldıktan sonra, bu
	 * bağlantı nesnesi üzerinden veri alış-verişi yapılabilinir.
	 * 
	 * @see de.muyat.bluetooth.IBaglanti#baslat()
	 */
	@Override
	public void baslat() {

        // Yerel bluetooth adaptoru yaratilir.
        bluetoothAdaptoru = BluetoothAdapter.getDefaultAdapter();
        // Eger adaptor nesnesi null ise, bluetooth bu cihazda desteklenmiyor demektir.
        if (bluetoothAdaptoru == null) {
        	// TODO: alt satirin düzeltimesi gerekli
			// Toast.makeText(this, R.string.bt_not_available, Toast.LENGTH_LONG).show();
			// TODO: Throw exception
            return;
        }
        
		uzakBluetoothCihazi = bluetoothAdaptoru.getRemoteDevice(uzakCihazinAdresi);
		
		LooperThread looperThread = new LooperThread();
		looperThread.start();
		baglantiBasladimi = true;
	}

	/**
	 * Bluetooth bağlantısını bitirir. Aynı bağlantıyı tekrardan başlatmak için
	 * başlat() methodu çağırılması yeterlidir.
	 * 
	 * @see de.muyat.bluetooth.IBaglanti#bitir()
	 */
	@Override
	public void bitir() {
		veriIsleyici.cancel();
		baglantiBasladimi = false;
	}

	/**
	 * Bluetooth bağlantısının başlatılıp başlatılmadığını bildirir.
	 * 
	 * @see de.muyat.bluetooth.IBaglanti#baglimi()
	 */
	@Override
	public boolean baglimi() {
		return baglantiBasladimi;
	}

	/**
	 * 
	 * 
	 * @see de.muyat.bluetooth.IBaglanti#veriAliciKaydet()
	 */
	@Override
	public void baglantiDinleyiciEkle(IBaglantiDinleyici veriAlici) {
		this.baglantiDinleyici = veriAlici;  
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.muyat.bluetooth.IBaglanti#veriYolla(byte[])
	 */
	@Override
	public boolean veriYolla(byte[] veri) {
		if (!baglantiBasladimi) {
			throw new YasalOlmayanBaglantiDurumHatasi();
		}

		if (veriIsleyici != null) {
			Message message = veriIsleyici.obtainMessage(MOVEMENT_DATA, veri);
			message.sendToTarget();
			return true;
		}

		return false;
	}

	/**
	 * Şuanki gerçekleştirim sadece bağlanılan cihazın adını döndürür.
	 * 
	 * @see de.muyat.bluetooth.IBaglanti#uzakCihazinBilgisiniAl()
	 */
	@Override
	public String uzakCihazinBilgisiniAl() {
		BluetoothDevice device = bluetoothAdaptoru
				.getRemoteDevice(uzakCihazinAdresi);

		return device.getName();
	}
	
	/**
	 * Veriyi işleyecek olan {@code VeriIsleyici} handler'ına ayrı bir thread
	 * atamak için bizim handler nesnemiz Looper.prepare() ve Looper.loop()
	 * methodları arasında yaratılır.
	 * 
	 * @author Fatih Ulusoy
	 */
	private class LooperThread extends Thread {

		public void run() {
			Looper.prepare();

			veriIsleyici = new BluetoothBaglantisiVeriIsleyici();
			
			Looper.loop();
		}
	}
	
	/**
	 * Bluetooth bağlantısından veri yollama işini yapan veri işleyici sınıfı.
	 * 
	 * @author Fatih Ulusoy
	 */
	private class BluetoothBaglantisiVeriIsleyici extends Handler {
		
        private InputStream inputStream;
        private OutputStream outputStream;
        private BluetoothSocket clientSocket;
        private GelenVeriDinleyici gelenVeriDinleyici;
        
		/**
		 * 
		 * @param device
		 */
		public BluetoothBaglantisiVeriIsleyici() {
			super();
        	soketYarat();
        	soketBaglantisiYarat();
        	createInputOutputStream();  
		}
		
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case INIT_HANDLER:

				break;
			case STOP_HANDLER:
				cancel();
				break;
			case MOVEMENT_DATA:
				
				byte[] buf = (byte[]) msg.obj;
				try {
					outputStream.write(buf, 0, buf.length);
					float[] accData = Kullanislilar.floatDizisineDonustur(buf, buf.length);					
					Log.v(TAG, "Sending:" + accData[0] + "," + accData[1] + ","
							+ accData[2] + accData[3] + "," + accData[4] + ","
							+ accData[5]);
				} catch (IOException e) {
					Log.e(TAG, "Error during the sensor data sending", e);
					break;
				}
				break;
			}

		}
        
        private void soketYarat() {
			// Uzak bluetooth cihazına soket açar.
			try {
				clientSocket = uzakBluetoothCihazi
						.createRfcommSocketToServiceRecord(servisUUID);
			} catch (IOException e) {
				Log.e(TAG, "Soket yaratilmasinda hata olustu!", e);
			}
        }
        
        /**
         * 
         */
        private void soketBaglantisiYarat() {
            Log.i(TAG, "BEGIN socket creation");

            // Make a connection to the BluetoothSocket
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
            	clientSocket.connect();
            } catch (IOException e) {
            	Log.e(TAG, "Unable to connect to the socket", e);
                // Close the socket
                try {
                	clientSocket.close();
                } catch (IOException e2) {
                    Log.e(TAG, "Unable to close() socket during connection failure", e2);
                }
                return;
            }
            Log.d(TAG, "Bluetooth socket connection was successfull.");
        }
        
        /**
         * 
         */
        private void createInputOutputStream() {
            Log.d(TAG, "Input & output streams are created.");
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get input and output streams for bluetooth connection
            try {
                tmpIn = clientSocket.getInputStream();
                tmpOut = clientSocket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error during the input & output stream creation.", e);
            }

            inputStream = tmpIn;
            outputStream = tmpOut;
            gelenVeriDinleyici = new GelenVeriDinleyici();
            gelenVeriDinleyici.run();
            
            Log.d(TAG, "Input & output streams were created successfully.");
        }

        public void cancel() {
            try {
            	gelenVeriDinleyici.bitir();
            	outputStream.write(EXIT_CMD);
            	outputStream.flush();
            	clientSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
        
        /***
         * 
         * 
         * @author Fatih Ulusoy
         */
    	private class GelenVeriDinleyici extends Thread {
    		
    		
    		private boolean calisiyorMu = false;
    		
    		/**
    		 * 
    		 */
    		public GelenVeriDinleyici() {
    			
    		}
    		
            public void run() {
                Log.i(TAG, "BEGIN mConnectedThread");
                int bytes;
                calisiyorMu = true;
                byte[] okunanVeri  = null;
                byte[] buffer = new byte[1024];
                
                // Keep listening to the InputStream while connected
                while (calisiyorMu) {
                    try {
                    	// Read from the InputStream
                        bytes = inputStream.read(buffer);
                        okunanVeri = new byte[bytes];
                        System.arraycopy(bytes, 0, okunanVeri, 0, bytes);
                        baglantiDinleyici.gelenVeriyiYolla(okunanVeri);
                    } catch (IOException e) {
                        Log.e(TAG, "disconnected", e);
                        connectionLost();
                        break;
                    }
                }
            }
            
            public void bitir() {
            	calisiyorMu = false;
            }
            
            /**
             * Indicate that the connection was lost and notify the UI Activity.
             */
            private void connectionLost() {


            }
            
    	}
        
	}
	

}


